# Copyright (c) 2008 Todd A. Fisher
# see LICENSE
require 'uri'
require 'timeout'
require 'net/http'
require 'mongrel'
require 'esi/logger'
require 'esi/cache'
require 'esi/config'
require 'esi/router'
require 'esi/processor'

module ESI

  class Proxy < Mongrel::HttpHandler
    attr_reader :config, :router
    include ESI::Log

    def process(request, response)
 
      start = Time.now
      status = 200
      url = @router.url_for(request.params["REQUEST_URI"])

      params = http_params(request.params)

      log_debug "#{request.params["REQUEST_METHOD"]} => #{url}"
      chunk_count = 0
      uri = URI.parse(url)

      path_with_query = uri.query ? "#{uri.path}?#{uri.query}" :  uri.path

      proxy_request = (request.params["REQUEST_METHOD"] == "POST") ?
                          Net::HTTP::Post.new( path_with_query, params ) :
                          Net::HTTP::Get.new( path_with_query, params )

      proxy_connection = Net::HTTP.start(uri.host, uri.port)

      # open the conneciton up so we can start to stream the connection
      proxy_connection.request(proxy_request,request.body.read) do|proxy_response|

        status = read_status( proxy_response )

        copy_headers( response.header, proxy_response ) unless status >= 500

        if status >= 500 or !@config.enable_esi_processor?( proxy_response )
          response.start(status, true) do|head,out|

            # proxy the 500 response
            proxy_response.read_body do|fragment|
              out << fragment
            end

          end
        else
          processor = Processor.new( @config, @router )
          processor.send_head( response, status )
          chunk_count = processor.send_body( request, params, response, proxy_response )
        end
      end # end request

    rescue => e
      STDERR.puts "\n#{e.message}: error at #{e.backtrace.first} msg at #{__FILE__}:#{__LINE__}\n"
    ensure
      log_request "\nCompleted => #{url}, #{Time.now - start} seconds with status #{status} and #{chunk_count} chunks\n"
    end

  protected

    def read_status(response)
      Net::HTTPResponse::CODE_TO_OBJ.select { |k,v| v == response.class }.first[0].to_i rescue 500
    end

    def http_params(params)
      updated_params = {}
      params.each do|k,v|
        k = k.split('_').collect { |t| t.capitalize }.join('-')
        if k[0,5] =='Http-'
          k[0,5] = ''
          updated_params[k] = v
        end
      end
      updated_params
    end

    def copy_headers(head,response)
      response.to_hash.each do |k,v|
        # for Set-Cookie we need to split on ,
        # some edge cases with , since things like expires might be a date with , in them.
        k = k.split(/-/).map{|s| s.capitalize }.join('-')
        if k == "Set-Cookie"
          v.each do|cookie|
            head["Set-Cookie"] = cookie.strip # mongrel is case sensitive about handling duplicates
          end
        else
          head[k] = v unless k == "Content-Length" or k == "Surrogate-Control" or k == "Server"
        end
      end
      head["Server"] = "MongrelESI 0.4"
    end

  end # Handler

end # ESI
